﻿using System.Data.Entity;
using Qi.Data.Contracts;

namespace Qi.Data.EntityFramework
{
    using System;
    using System.Linq;
    using System.Data.Entity.Infrastructure;
    using System.Data;
    using System.Linq.Expressions;

    public class EntityFrameworkRepository<TId, TEntity> : IRepository<TId, TEntity> where TEntity : class
    {
        public EntityFrameworkRepository(DbContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            this.DbContext = context;
            this.DbSet = this.DbContext.Set<TEntity>();
        }

        private DbContext DbContext { get; set; }

        private DbSet<TEntity> DbSet { get; set; }

        public IQueryable<TEntity> GetAll(params Expression<Func<TEntity, object>>[] includes)
        {
            IQueryable<TEntity> query = this.DbSet;

            if (includes != null)
            {
                query = includes.Aggregate(query, (current, include) => current.Include(include));
            }

            return query;
        }

        public TEntity Get(TId id)
        {
            return this.DbSet.Find(id);
        }

        public void Add(TEntity entity)
        {
            var dbEntityEntry = this.DbContext.Entry(entity);
            if (dbEntityEntry.State != EntityState.Detached)
            {
                dbEntityEntry.State = EntityState.Added;
                return;
            }

            this.DbSet.Add(entity);
        }

        public void Update(TEntity entity)
        {
            var dbEntityEntry = this.DbContext.Entry(entity);
            if (dbEntityEntry.State == EntityState.Detached)
            {
                this.DbSet.Attach(entity);
            }

            dbEntityEntry.State = EntityState.Modified;
        }

        public void Delete(TEntity entity)
        {
            var dbEntityEntry = this.DbContext.Entry(entity);
            if (dbEntityEntry.State != EntityState.Deleted)
            {
                dbEntityEntry.State = EntityState.Deleted;
            }
            else
            {
                this.DbSet.Attach(entity);
                this.DbSet.Remove(entity);
            }
        }

        public void Delete(TId id)
        {
            // TODO: check if implements IEntity
            var entity = this.Get(id);
            if (entity == null)
            {
                return;
            }
            
            this.Delete(entity);
        }
    }

    public class EntityFrameworkRepository<TEntity> : EntityFrameworkRepository<int, TEntity>, IRepository<TEntity> 
        where TEntity : class
    {
        public EntityFrameworkRepository(DbContext context) : base(context)
        {
        }
    }
}